package com.account.book.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.account.book.common.constant.CacheConstant;
import com.account.book.common.constant.ConstantPool;
import com.account.book.common.context.UserTokenContextHolder;
import com.account.book.common.exception.BasicInfoException;
import com.account.book.common.exception.BasicInfoStatusEnum;
import com.account.book.dto.UserDTO;
import com.account.book.dto.UserLoginDTO;
import com.account.book.entity.BookMoney;
import com.account.book.entity.BookUser;
import com.account.book.entity.User;
import com.account.book.enums.DataStatusEnum;
import com.account.book.mapper.BookMoneyMapper;
import com.account.book.mapper.BookUserMapper;
import com.account.book.mapper.UserMapper;
import com.account.book.request.UpdateUserInfo;
import com.account.book.request.UserLoginReqVo;
import com.account.book.request.UserNameReqVo;
import com.account.book.response.UserLoginRespVo;
import com.account.book.service.BookUserService;
import com.account.book.service.UserService;
import com.account.book.vo.UserTokenVO;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import me.chanjar.weixin.common.error.WxErrorException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * @author liang-hw
 * @className UserServiceImpl
 * @description 用户表（微信）表 服务实现类
 * @date 2023/11/08 11:42:11
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
    @Autowired
    private WxMaService wxMaService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private UserService userService;

    @Autowired
    private BookUserService bookUserService;

    @Autowired
    private BookUserMapper bookUserMapper;
    @Autowired
    private BookMoneyMapper bookMoneyMapper;

    /**
     * @param @param userLoginReqVO
     * @return @return {@link UserLoginRespVo }
     * @author liang-hw
     * @description 用户登录
     * @date 2023/11/19 18:00:48
     */
    @Override
    public UserLoginRespVo userLogin(UserLoginReqVo userLoginReqVO) {
        // 请求微信 auth.code2Session 接口，换取用户唯一标识 openid
        String openid = "";
        String sessionKey = "";
        try {
            WxMaJscode2SessionResult wxMaJscode2SessionResult = wxMaService.getUserService()
                    .getSessionInfo(userLoginReqVO.getCode());
            openid = wxMaJscode2SessionResult.getOpenid();
            sessionKey = wxMaJscode2SessionResult.getSessionKey();
        } catch (WxErrorException e) {
            logger.error("请求微信接口code2Session，获取用户唯一标识 openid异常:{}", e.fillInStackTrace());
            throw new BasicInfoException(BasicInfoStatusEnum.THIRD_SERVICE_UNAVAILABLE.getCode(), "请求微信服务失败");
        }

        UserLoginDTO userLoginDTO = new UserLoginDTO().setOpenId(openid);

        userLoginDTO.setAvatarUrl("https://thirdwx.qlogo.cn/mmopen/vi_32/POgEwh4mIHO4nibH0KlMECNjjGxQUq24ZEaGT4poC6icRiccVGKSyXwibcPq4BWmiaIGuG1icwxaQX6grC9VemZoJ8rg/132").setNickName("微信用户")
                .setGender(0);
        UserDTO userDTO = userLogin(userLoginDTO);
        // 用户重新登录后，移除用户历史token和历史sessionKey
        String historyToken = (String) redisTemplate.opsForValue()
                .get(ConstantPool.getCachePrefix() + StrUtil.COLON + CacheConstant.OPENID_KEY + openid);
        if (StrUtil.isNotBlank(historyToken)) {
            redisTemplate.delete(ConstantPool.getCacheTokenName() + StrUtil.COLON + historyToken);
            redisTemplate.delete(
                    ConstantPool.getCachePrefix() + StrUtil.COLON + CacheConstant.USER_SESSION_KEY + historyToken);
        }
        // token 保存到缓存，有效时间30天
        redisTemplate.opsForValue().set(
                ConstantPool.getCachePrefix() + StrUtil.COLON + CacheConstant.OPENID_KEY + openid, userDTO.getToken(),
                30, TimeUnit.DAYS);
        UserTokenVO userTokenVO = new UserTokenVO().setUserId(userDTO.getId()).setOpenid(openid)
                .setToken(userDTO.getToken());
        String tokenCacheKey = ConstantPool.getCacheTokenName() + StrUtil.COLON + userTokenVO.getToken();
        redisTemplate.opsForValue().set(tokenCacheKey, JSONUtil.toJsonStr(userTokenVO), 30, TimeUnit.DAYS);
        // sessionKey 保存到缓存，有效时间3天
        String sessionKeyCacheKey = ConstantPool.getCachePrefix() + StrUtil.COLON + CacheConstant.USER_SESSION_KEY
                + userTokenVO.getToken();
        redisTemplate.opsForValue().set(sessionKeyCacheKey, sessionKey, 3, TimeUnit.DAYS);
        UserLoginRespVo userLoginRespVO = BeanUtil.copyProperties(userDTO, UserLoginRespVo.class);
        logger.info("登录响应数据：{}", JSON.toJSONString(userLoginRespVO));
        return userLoginRespVO;
    }

    /**
     * @param @param UpdateUserInfo
     * @return @return {@link UserLoginRespVo }
     * @author liang-hw
     * @description 修改用户昵称，头像
     * @date 2023/11/19 18:01:13
     */
    @Override
    @Transactional
    public UserLoginRespVo updateUserInfo(UpdateUserInfo updateUserInfo, String userId) {
        logger.info("修改用户昵称，头像请求数据信息：" + JSON.toJSONString(updateUserInfo));
        UserLoginRespVo userLoginRespVO = new UserLoginRespVo();
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().eq(User::getId, updateUserInfo.getId())
                .eq(User::getOpenId, updateUserInfo.getOpenId());
        User user = new User();
        BeanUtil.copyProperties(updateUserInfo, user);
        baseMapper.update(user, updateWrapper);
        //更新账本用户信息
        BookUser bookUser = new BookUser();
        bookUser.setUserName(updateUserInfo.getNickName());
        bookUser.setAvatarUrl(updateUserInfo.getAvatarUrl());
        bookUser.setReallyName(updateUserInfo.getNickName());
        bookUser.setUpdateBy(userId);
        UpdateWrapper<BookUser> bookUserUpdateWrapper = new UpdateWrapper<>();
        bookUserUpdateWrapper.lambda().eq(BookUser::getUserId, updateUserInfo.getId());
        bookUserMapper.update(bookUser, bookUserUpdateWrapper);
        //更新账单
        BookMoney bookMoney = new BookMoney();
        bookMoney.setUserName(updateUserInfo.getNickName());
        bookMoney.setUpdateBy(userId);
        UpdateWrapper<BookMoney> bookMoneyUpdateWrapper = new UpdateWrapper<>();
        bookMoneyUpdateWrapper.lambda().eq(BookMoney::getCreateBy, userId)
                .eq(BookMoney::getStatus, DataStatusEnum.ENABLE.getValue());
        bookMoneyMapper.update(bookMoney, bookMoneyUpdateWrapper);
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(User::getId, updateUserInfo.getId())
                .eq(User::getOpenId, updateUserInfo.getOpenId());
        User userOne = baseMapper.selectOne(queryWrapper);
        BeanUtil.copyProperties(userOne, userLoginRespVO);
        return userLoginRespVO;
    }

    /**
     * 修改用户别名,真实姓名
     *
     * @param userNameReqVo
     */
    @Override
    public void updateReallyNameAndUserName(UserNameReqVo userNameReqVo) {
        User byId = this.getById(userNameReqVo.getBookId());
        if (byId == null) {
            throw new BasicInfoException(BasicInfoStatusEnum.PARAM_ERROR.getCode(), "用户不存在");
        }

        UserTokenVO userTokenVOByToken = UserTokenContextHolder.getUserTokenVOByToken();

        // 是否是本人操作
        boolean isUser = userTokenVOByToken.getOpenid().equals(byId.getOpenId());

        // 操作人是否是管理员
        BookUser bookUser = bookUserService.getOne(new QueryWrapper<BookUser>().lambda()
                .eq(BookUser::getBookId, userNameReqVo.getBookId())
                .eq(BookUser::getUserId, userNameReqVo.getUserId()));
        if (null == bookUser) {
            throw new BasicInfoException(BasicInfoStatusEnum.PARAM_ERROR.getCode(), "账本不存在该用户");
        }
        // 是否是管理员权限
        boolean isRoot = "1".equals(bookUser.getAuth());

        if (isRoot || isUser) {
            bookUser.setUpdateBy(byId.getId());
            bookUser.setReallyName(userNameReqVo.getReallyName());
            bookUser.setUserName(userNameReqVo.getUserName());
            bookUserService.updateById(bookUser);
        }

    }

    public UserDTO userLogin(UserLoginDTO userLoginDTO) {
        User user = userService.getOne(new QueryWrapper<User>().lambda().eq(User::getOpenId, userLoginDTO.getOpenId()));
        if (null == user) {
            user = new User();
            BeanUtil.copyProperties(userLoginDTO, user, false);
            if (StrUtil.isNotBlank(userLoginDTO.getNickName())) {
                user.setNickName(userLoginDTO.getNickName().trim());
            }
            user.setCreateTime(new Date());
            userService.save(user);
        }
        String token = IdUtil.fastSimpleUUID() + RandomUtil.randomString(8);
        return BeanUtil.copyProperties(user, UserDTO.class).setToken(token);
    }
}
